﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace WebSocket.Demo
{


    #region <WebSocket委托定义>

    // 异步接受客户端连接（握手）的方法代理
    using WebSocketAccept =
        Action<IDictionary<string, object>, //Accept字典，可以为null
        Func<                               //握手成功后的回调函数
            IDictionary<string, object>,       //包含 SendAsync, ReceiveAsync, CloseAsync 等关键字的字典
            Task>                              //返回给服务器的表示本回调函数是否执行完成的字典
        >;


    // 异步关闭连的函数代理
    using WebSocketCloseAsync =
    Func<int,                   //关闭的状码代码
        string,                 //说明
        CancellationToken,      //任务是否取消
        Task                    //代表本操作是否完成的任务
    >;

    // 异步读取数据的函数代理
    using WebSocketReceiveAsync =
        Func<ArraySegment<byte>, // 接受数据的缓冲区
            CancellationToken,   // 传递操作是否取消
            Task<                // 返回值
                Tuple<
                    int,      // 第一分量，表示接收到的数据类型（1表示本文数据，2表示二进制数据，8表示对方关闭连接）
                    bool,     // 第二分量，表示是否是一个数据帖的最后一块或者独立块
                    int       // 第三分量，表示有效数据的长度
                >
            >
        >;


    // 异步发送数据的函数代表
    using WebSocketSendAsync =
        Func<ArraySegment<byte>,     // 待发送的缓冲区
            int,                     // 数据类型，只能是1、2、8
            bool,                    // 这一块数据是否是一条信息的最后一块
            CancellationToken,       // 取消任务的通知
            Task                     // 返回值
        >;


    #endregion



    class ClientDemo
    {

        /// <summary>
        /// 进行连接的函数
        /// </summary>
        WebSocketAccept accept;


        /// <summary>
        /// 发送数据的函数
        /// </summary>
        WebSocketSendAsync sendAsync;

        /// <summary>
        /// 接收数据的函数
        /// </summary>
        WebSocketReceiveAsync receiveAsync;

        /// <summary>
        /// 关闭连接的函数
        /// </summary>
        WebSocketCloseAsync closeAsync;



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="owinEnv"></param>
        public ClientDemo(IDictionary<string, object> owinEnv)
        {
            // 获取Accept方法
            accept = owinEnv.Get<WebSocketAccept>("websocket.Accept");
        }


        /// <summary>
        /// 响应客户端握手请求
        /// </summary>
        /// <returns>返回真表示按websocket的方式连接</returns>
        public bool Accept()
        {
            if (accept == null) return false;

            //连接
            accept(null, sckEnv =>
            {
                //从字典中取出服务器提供的操作函数
                sendAsync = sckEnv.Get<WebSocketSendAsync>("websocket.SendAsync");
                receiveAsync = sckEnv.Get<WebSocketReceiveAsync>("websocket.ReceiveAsync");
                closeAsync = sckEnv.Get<WebSocketCloseAsync>("websocket.CloseAsync");

                //开始交互
                ReadData(null);

                //返回一个任务，表示本函数已经操作完成
                return Task.Delay(0);
            });

            //表示是websocket消息并同意握手
            return true;

        }



        /// <summary>
        /// 接收客户端发送过来的数据
        /// </summary>
        /// <param name="tastSend">完成的发送任务</param>
        void ReadData(Task tastSend)
        {
            /*** 本方法不要进行阻塞操作 ****/

            //检看上次发送事件是否失败
            if (tastSend != null && (tastSend.IsCanceled || tastSend.IsFaulted))
            {
                if (tastSend.IsFaulted) { var laste = tastSend.Exception; }
                return;
            }


            // 开始读取数据
            /////////////////////////////////////////////////////////

            var buffer = new ArraySegment<byte>(new byte[8192]);
            var taskReceive = receiveAsync(buffer, CancellationToken.None);

            //读数据完成后进行应答操作
            taskReceive.ContinueWith(t => SendEchoData(t, buffer));

        }

        /// <summary>
        /// 应答操作
        /// </summary>
        /// <param name="taskRead">已完成的接收任务</param>
        /// <param name="buffer">接收的数据</param>
        void SendEchoData(Task<Tuple<int, bool, int>> taskRead, ArraySegment<byte> buffer)
        {

            /*** 本方法不要进行阻塞式的操作 ***/

            if (taskRead.IsFaulted || taskRead.IsCanceled)
            {
                Console.WriteLine("Remote Closed...");         //读取操作被取消或失败了
                if (taskRead.IsFaulted) { var tre = taskRead.Exception; }  //如果是异常，就把异常对象取出来

                //接收有问题，不再继续
                return;
            }

            //获取得到的数据
            var x = taskRead.Result;

            //如果是客户端发送的close信号
            if (x.Item1 == 0x8)
            {
                Console.WriteLine("The client has been disconnected.");
                return;
            }

            //把数据复制出来
            var len = x.Item3; //第三分量表示读得的长度
            var msgByts = new byte[len];
            Buffer.BlockCopy(buffer.Array, 0, msgByts, 0, len);
            var msgTxt = Encoding.UTF8.GetString(msgByts);

            //如果用户主动要求退出
            if (msgTxt == "close")
            {
                closeAsync(1000, null, CancellationToken.None); //1000表示正常断开
                return;
            }


            // 向端户端发送应答数据
            var taskSend = sendAsync(new ArraySegment<byte>(buffer.Array, 0, x.Item3), x.Item1, x.Item2, CancellationToken.None);

            //发送完成后，再次读取
            taskSend.ContinueWith(t => ReadData(t));

        }



    }


    internal static class DictionaryExtensions
    {
        internal static T Get<T>(this IDictionary<string, object> dictionary, string key)
        {
            object value;
            return dictionary.TryGetValue(key, out value) ? (T)value : default(T);
        }
    }



}
